package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.common.exception.BusinessException;
import com.atguigu.srb.core.enums.LendStatusEnum;
import com.atguigu.srb.core.enums.ReturnMethodEnum;
import com.atguigu.srb.core.enums.TransTypeEnum;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.mapper.UserAccountMapper;
import com.atguigu.srb.core.mapper.UserInfoMapper;
import com.atguigu.srb.core.pojo.bo.TransFlowBO;
import com.atguigu.srb.core.pojo.entity.*;
import com.atguigu.srb.core.mapper.LendMapper;
import com.atguigu.srb.core.pojo.vo.BorrowInfoApprovalVO;
import com.atguigu.srb.core.pojo.vo.BorrowerDetailVO;
import com.atguigu.srb.core.service.*;
import com.atguigu.srb.core.util.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 标的准备表 服务实现类
 * </p>
 *
 * @author Bright
 * @since 2022-04-09
 */
@Slf4j
@Service
public class LendServiceImpl extends ServiceImpl<LendMapper, Lend> implements LendService {

    @Resource
    DictService dictService;

    @Resource
    BorrowerService borrowerService;

    @Resource
    UserInfoMapper userInfoMapper;

    @Resource
    UserAccountMapper userAccountMapper;

    @Resource
    TransFlowService transFlowService;

    @Resource
    LendItemService lendItemService;

    @Resource
    LendReturnService lendReturnService;

    @Resource
    LendItemReturnService lendItemReturnService;

    @Resource
    LendMapper lendMapper;


    @Override
    public void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo) {

        Lend lend = new Lend();
        //标的编号
        lend.setLendNo(LendNoUtils.getLendNo());
        //借款用户id
        lend.setUserId(borrowInfo.getUserId());
        //借款信息id
        lend.setBorrowInfoId(borrowInfo.getId());
        lend.setAmount(borrowInfo.getAmount());
        lend.setReturnMethod(borrowInfo.getReturnMethod());
        lend.setPeriod(borrowInfo.getPeriod());

        lend.setTitle(borrowInfoApprovalVO.getTitle());
        lend.setLendYearRate(borrowInfoApprovalVO.getLendYearRate().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
        lend.setServiceRate(borrowInfoApprovalVO.getServiceRate().divide(new BigDecimal(100), 4, RoundingMode.HALF_UP));
        lend.setLowestAmount(new BigDecimal(100));
        lend.setInvestAmount(new BigDecimal(0));
        lend.setInvestNum(0);
        lend.setPublishDate(LocalDateTime.now());

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate lendStartDate = LocalDate.parse(borrowInfoApprovalVO.getLendStartDate(), dateTimeFormatter);

        lend.setLendStartDate(lendStartDate);

        //结束日期
        Integer period = borrowInfo.getPeriod();
        LocalDate lenEndDate = lendStartDate.plusMonths(period);
        lend.setLendEndDate(lenEndDate);
        //平台预期收益
        //月服务利率*期数*借款金额
        lend.setLendInfo(borrowInfoApprovalVO.getLendInfo());
        BigDecimal monthRate = borrowInfoApprovalVO.getServiceRate().divide(new BigDecimal(12), 8, RoundingMode.DOWN);
        BigDecimal expectAmount = monthRate.multiply(new BigDecimal(period)).multiply(borrowInfo.getAmount());
        lend.setExpectAmount(expectAmount);
        //实际收益
        lend.setRealAmount(new BigDecimal(0));

        //状态
        lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());
        //审核时间
        lend.setCheckTime(LocalDateTime.now());
        //审核人
        lend.setCheckAdminId(1L);


        baseMapper.insert(lend);
    }

    @Override
    public List<Lend> getList() {
        List<Lend> lends = baseMapper.selectList(null);
        lends.forEach(lend -> {
            lend.getParam().put("returnMethod", dictService.getDictNamebyValueAndDictCode(lend.getReturnMethod(), "returnMethod"));
            lend.getParam().put("status", LendStatusEnum.getMsgByStatus(lend.getStatus()));

        });
        return lends;
    }

    @Override
    public Map<String, Object> getLendDetailById(Long id) {
        Lend lend = baseMapper.selectById(id);
        lend.getParam().put("returnMethod", dictService.getDictNamebyValueAndDictCode(lend.getReturnMethod(), "returnMethod"));
        lend.getParam().put("status", LendStatusEnum.getMsgByStatus(lend.getStatus()));
        QueryWrapper<Borrower> borrowerQueryWrapper = new QueryWrapper<>();
        borrowerQueryWrapper.eq("user_id", lend.getUserId());

        Borrower borrower = borrowerService.getOne(borrowerQueryWrapper);
        BorrowerDetailVO borrowerDetailVO = borrowerService.getBorrowerVoById(borrower.getId());
        HashMap<String, Object> lendDetail = new HashMap<>(2);

        lendDetail.put("lend", lend);
        lendDetail.put("borrower", borrowerDetailVO);

        return lendDetail;
    }

    @Override
    public Lend lendById(Long id) {
        Lend lend = baseMapper.selectById(id);
        lend.getParam().put("returnMethod", dictService.getDictNamebyValueAndDictCode(lend.getReturnMethod(), "returnMethod"));
        lend.getParam().put("status", LendStatusEnum.getMsgByStatus(lend.getStatus()));
        return lend;
    }

    @Override
    public BigDecimal getInterestCount(BigDecimal invest, BigDecimal yearRate, Integer totalmonth, Integer returnMethod) {

        BigDecimal interestCount;
        //计算总利息
        if (returnMethod.intValue() == ReturnMethodEnum.ONE.getMethod()) {
            interestCount = Amount1Helper.getInterestCount(invest, yearRate, totalmonth);
        } else if (returnMethod.intValue() == ReturnMethodEnum.TWO.getMethod()) {
            interestCount = Amount2Helper.getInterestCount(invest, yearRate, totalmonth);
        } else if (returnMethod.intValue() == ReturnMethodEnum.THREE.getMethod()) {
            interestCount = Amount3Helper.getInterestCount(invest, yearRate, totalmonth);
        } else {
            interestCount = Amount4Helper.getInterestCount(invest, yearRate, totalmonth);
        }
        return interestCount;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void makeLoan(Long lendId) {
        //获取标的信息
        Lend lend = baseMapper.selectById(lendId);
        //放款接口调用
        HashMap<String, Object> paramMap = new HashMap<>(7);
        paramMap.put("agentId", HfbConst.AGENT_ID);
        paramMap.put("agentProjectCode", lend.getLendNo());
        String lendNo = LendNoUtils.getLendNo();
        paramMap.put("agentBillNo", lendNo);
        //平台收益，放款扣除，借款人借款实际金额=借款金额-平台收益
        //月年化
        BigDecimal serviceRate = lend.getServiceRate().divide(new BigDecimal(12), 4, RoundingMode.HALF_UP);
        //已投金额
        BigDecimal investAmount = lend.getInvestAmount();
        //平台实际收益 = 已投金额 * 月年化 * 标的期数
        BigDecimal mchFee = serviceRate.multiply(investAmount).multiply(BigDecimal.valueOf(lend.getPeriod()));
        //商户手续费(平台实际收益)
        paramMap.put("mchFee", mchFee);
        paramMap.put("note", "无");
        paramMap.put("timestamp", RequestHelper.getTimestamp());
        paramMap.put("sign", RequestHelper.getSign(paramMap));

        //发送同步远程调用
        JSONObject result = RequestHelper.sendRequest(paramMap, HfbConst.MAKE_LOAD_URL);
        log.info("放款结果" + result.toJSONString());

        //放款失败
        String resultCode = result.getString("resultCode");
        if (!HfbConst.SUCCESSFUL_LENDING.equals(resultCode)) {
            throw new BusinessException(result.getString("resultMsg"));
        }
        //更新标的信息:(放款时间,放款人id,状态,实际收益)
        lend.setStatus(LendStatusEnum.PAY_RUN.getStatus());
        lend.setPaymentAdminId(1L);
        lend.setPaymentTime(LocalDateTime.now());
        //平台实际收益
        lend.setRealAmount(mchFee);

        lendMapper.updateById(lend);
        //获取借款人信息
        UserInfo userInfo = userInfoMapper.selectById(lend.getUserId());
        //给借款账号转入金额
        BigDecimal voteAmt = new BigDecimal(result.getString("voteAmt"));
        String bindCode = userInfo.getBindCode();
        userAccountMapper.updateAccount(bindCode, voteAmt, new BigDecimal(0));

        //新增借款人交易流水
        TransFlowBO transFlowBO = new TransFlowBO(
                lendNo,
                bindCode,
                voteAmt,
                TransTypeEnum.BORROW_BACK,
                result.getString("resultMsg"));
        transFlowService.saveTransFlow(transFlowBO);
        //获取投资列表信息
        for (LendItem lendItem : lendItemService.selectByLendId(lendId, 1)) {
            //获取投资人信息
            UserInfo investUser = userInfoMapper.selectById(lendItem.getInvestUserId());
            String investBindCode = investUser.getBindCode();
            BigDecimal investAmountItem = lendItem.getInvestAmount();
            //投资人账号冻结金额转出
            userAccountMapper.updateAccount(investBindCode, new BigDecimal(0), investAmountItem.negate());
            //新增投资人交易流水
            TransFlowBO investTransFlowBO = new TransFlowBO(
                    LendNoUtils.getTransNo(),
                    investBindCode,
                    investAmountItem,
                    TransTypeEnum.INVEST_UNLOCK,
                    result.getString("resultMsg"));

            transFlowService.saveTransFlow(investTransFlowBO);

        }


        //放款成功生成借款人还款计划和投资人回款计
        this.repaymentPlan(lend);


    }

    @Override
    public void repaymentPlan(Lend lend) {
        ArrayList<LendReturn> lendReturns = new ArrayList<>();
        //分批次创建还款计划
        for (int i = 1; i <= lend.getPeriod(); i++) {
            LendReturn lendReturn = new LendReturn();
            lendReturn.setUserId(lend.getUserId());
            lendReturn.setLendId(lend.getId());
            lendReturn.setCreateTime(LocalDateTime.now());
            lendReturn.setStatus(LendReturn.NOT_RETURNED);
            lendReturn.setLendYearRate(lend.getLendYearRate());
            lendReturn.setBorrowInfoId(lend.getBorrowInfoId());
            lendReturn.setReturnMethod(lend.getReturnMethod());
            lendReturn.setCurrentPeriod(i);

            //还款批次号
            lendReturn.setReturnNo(LendNoUtils.getReturnNo());

            LocalDate lendStartDate = lend.getLendStartDate();
            LocalDate returnDate = lendStartDate.plusMonths(i);
            lendReturn.setFee(new BigDecimal(0));
            //还款日期
            lendReturn.setReturnDate(returnDate);
            //计息本金额
            lendReturn.setBaseAmount(lend.getInvestAmount());
            //借款金额
            lendReturn.setAmount(lend.getAmount());


            lendReturn.setLast(i >= lend.getPeriod());
            lendReturns.add(lendReturn);
        }


        //批量保存
        lendReturnService.saveBatch(lendReturns);

        //获取lendReturnList中还款期数与还款计划id对应map
        Map<Integer, Long> lendReturnIdMap = lendReturns.stream().collect(
                Collectors.toMap(LendReturn::getCurrentPeriod, LendReturn::getId));


        List<LendItemReturn> lendItemReturns = this.returnInvest(lend, lendReturnIdMap);
        lendReturns.forEach(lendReturn -> {
            BigDecimal sumPrincipal = lendItemReturns
                    .stream()
                    .filter(lendItemReturn -> lendItemReturn.getLendReturnId().equals(lendReturn.getId()))
                    .map(LendItemReturn::getPrincipal)
                    .reduce(new BigDecimal(0), BigDecimal::add);

            BigDecimal sumInterest = lendItemReturns
                    .stream()
                    .filter(lendItemReturn -> lendItemReturn.getLendReturnId().equals(lendReturn.getId()))
                    .map(LendItemReturn::getInterest)
                    .reduce(new BigDecimal(0), BigDecimal::add);

            BigDecimal sumTotal = lendItemReturns.stream()
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getTotal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            lendReturn.setInterest(sumInterest);
            lendReturn.setPrincipal(sumPrincipal);
            lendReturn.setTotal(sumTotal);

        });
        lendReturnService.updateBatchById(lendReturns);


    }

    @Override
    public List<LendItemReturn> returnInvest(Lend lend, Map<Integer, Long> lendReturnIdMap) {
        //获取所有投资者
        List<LendItem> lendItems = lendItemService.selectByLendId(lend.getId(), LendItem.PAID);
        BigDecimal lendYearRate = lend.getLendYearRate();
        Integer period = lend.getPeriod();
        Integer returnMethod = lend.getReturnMethod();

        ArrayList<LendItemReturn> lendItemReturns = new ArrayList<>();
        lendItems.forEach(lendItem -> {
            BigDecimal investAmount = lendItem.getInvestAmount();

            Map<Integer, BigDecimal> perMonthPrincipal = AmountHelper.getPerMonthPrincipal(investAmount, lendYearRate, period, returnMethod);
            Map<Integer, BigDecimal> perMonthInterest = AmountHelper.getPerMonthInterest(investAmount, lendYearRate, period, returnMethod);


            for (int currentPeriod = 1; currentPeriod <= period; currentPeriod++) {
                LendItemReturn lendItemReturn = new LendItemReturn();
                //标的的返还id
                lendItemReturn.setLendReturnId(lendReturnIdMap.get(currentPeriod));
                lendItemReturn.setLendItemId(lendItem.getId());
                lendItemReturn.setLendId(lend.getId());
                lendItemReturn.setInvestAmount(investAmount);
                lendItemReturn.setCurrentPeriod(currentPeriod);
                lendItemReturn.setLendYearRate(lendItem.getLendYearRate());
                lendItemReturn.setReturnMethod(returnMethod);
                lendItemReturn.setInvestUserId(lendItem.getInvestUserId());
                //本金
                lendItemReturn.setPrincipal(perMonthPrincipal.get(currentPeriod));
                //利息
                lendItemReturn.setInterest(perMonthInterest.get(currentPeriod));

                //本息
                lendItemReturn.setTotal(lendItemReturn.getInterest().add(lendItemReturn.getPrincipal()));
                lendItemReturn.setStatus(LendItemReturn.NOT_RETURNED);

                lendItemReturn.setFee(new BigDecimal(0));
                lendItemReturn.setReturnDate(lend.getLendStartDate().plusMonths(currentPeriod));
                lendItemReturns.add(lendItemReturn);
            }
        });

        lendItemReturnService.saveBatch(lendItemReturns);
        return lendItemReturns;
    }


}
